#include "gplib.h"
#include <malloc.h>

const int g_nByteOffset[4] = { 3, 1, -1, -3 };
const int g_nMaxPairsOfCoord = 24;

/**
 * Проверка координат точки на корректность
 * @param указатель на графический контекст
 * @param координата по X
 * @param координата по Y
 * @param имя цункции вызвавшей проверку
 * @return код ошибки
 */
inline int PointAssert(TGraphicContext *ctx, int x, int y, char *pszFunc) {
	if (ctx == NULL) {
		debug(("%s *Error: ctx == NULL\n", pszFunc));
		return GP_ERR;
	}
	if (x < 0) {
		debug(("%s *Error: x < 0, x = %d\n", pszFunc, x));
		return GP_ERR;
	}
	if (y < 0) {
		debug(("%s *Error: y < 0, y = %d\n", pszFunc, y));
		return GP_ERR;
	}
	if (x >= ctx->nScreenSizeX) {
		debug(("%s *Error: x < ctx->nScreenSizeX, x = %d\n", pszFunc, x));
		return GP_ERR;
	}
	if (y >= ctx->nScreenSizeY) {
		debug(("%s *Error: y < ctx->nScreenSizeY, y = %d\n", pszFunc, y));
		return GP_ERR;
	}

	return GP_OK;
}

/**
 * Инициализация графического контекста
 * @param указатель на графический контекст
 * @param указатель на область ОЗУ модуля УПД32-А, куда будет производиться синтез графических объектов
 * @param размер области ОЗУ в байтах
 * @param ширина экрана в пикселях
 * @param высота экрана в пикселях
 * @param флаг необходимости изменения порядка байт
 * @param число бит на пиксель
 * @return код ошибки
 */
int InitGContext(TGraphicContext *ctx, char *pData, size_t nSize, int nScreenSizeX, int nScreenSizeY,
		char bReorder, char nBitsPerPixel) {
	int i;

	if (ctx == NULL) {
		debug(("InitGContext *Error: ctx == NULL\n"));
		return GP_ERR;
	}
	if (nSize < nScreenSizeX * nScreenSizeY) {
		debug(("InitGContext *Error: nSize < nScreenSizeX * nScreenSizeY\n"));
		return GP_ERR;
	}
	if (nBitsPerPixel != 8 && nBitsPerPixel != 16 && nBitsPerPixel != 24) {
		debug(("InitGContext *Error: Invalid nBitsPerPixel value\n"));
		return GP_ERR;
	}

	ctx->pData = pData;
	ctx->nSize = nSize;
	ctx->nScreenSizeX = nScreenSizeX;
	ctx->nScreenSizeY = nScreenSizeY;
	ctx->nBytesPerLine = nScreenSizeX * (nBitsPerPixel / 8);
	ctx->nLastPositionX = 0;
	ctx->nLastPositionY = 0;
	ctx->pLastScreenPos = pData;
	ctx->bReorder = bReorder;
	ctx->nBitsPerPixel = nBitsPerPixel;
	ctx->nLineMask = -1;
	ctx->nCurrentPos = 1;
	ctx->nLineMaskPos = 0;
	ctx->nCurrentThickness = 1;

	ctx->nPointsANumber = g_nMaxPairsOfCoord << 1;
	ctx->ppPointsArray = (char **)malloc(nScreenSizeY * ctx->nPointsANumber * sizeof(char **));
	ctx->pppRowIndexes = (char ***)malloc(nScreenSizeY * sizeof(char ***));

	for (i = 0; i < nScreenSizeY; i++) {
		ctx->pppRowIndexes[i] = ctx->ppPointsArray + i * ctx->nPointsANumber;
	}

	return GP_OK;
}

/**
 * Изменение текущих координат
 * @param указатель на графический контекст
 * @param координата по X
 * @param координата по Y
 * @return код ошибки
 */
int MoveTo(TGraphicContext *ctx, int nX, int nY) {
	if (PointAssert(ctx, nX, nY, "LineTo") != GP_OK) {
		return GP_ERR;
	}

	ctx->nLastPositionX = nX;
	ctx->nLastPositionY = nY;

	switch (ctx->nBitsPerPixel) {
	case 8:
		ctx->pLastScreenPos = GET_PIXEL_PTR_8(ctx->pData, nX, nY, ctx->nBytesPerLine);
		break;
	case 16:
		ctx->pLastScreenPos = GET_PIXEL_PTR_16(ctx->pData, nX, nY, ctx->nBytesPerLine);
		break;
	case 24:
		ctx->pLastScreenPos = GET_PIXEL_PTR_24(ctx->pData, nX, nY, ctx->nBytesPerLine);
		break;
	}

	return GP_OK;
}

/**
 * Рисование точки
 * @param указатель на графический контекст
 * @param координата по X
 * @param координата поS Y
 * @param цвет
 * @return код ошибки
 */
int SetPixel(TGraphicContext *ctx, int nX, int nY, TColor Color) {
	char bReorder = ctx->bReorder;

	if (PointAssert(ctx, nX, nY, "LineTo") != GP_OK) {
		return GP_ERR;
	}

	switch (ctx->nBitsPerPixel) {
	case 8:
		SET_PIXEL_8(GET_PIXEL_PTR_8(ctx->pData, nX, nY, ctx->nBytesPerLine), &Color)
		break;
	case 16:
		SET_PIXEL_16(GET_PIXEL_PTR_16(ctx->pData, nX, nY, ctx->nBytesPerLine), &Color)
		break;
	case 24:
		SET_PIXEL_24(GET_PIXEL_PTR_24(ctx->pData, nX, nY, ctx->nBytesPerLine), &Color)
		break;
	}


	return GP_OK;
}

/**
 * Сброс текущей позиции анализирования маски для функций отрисовки линий
 * @param указатель на графический контекст
 * @return код ошибки
 */
int ResetCurrentPos(TGraphicContext *ctx) {
	ctx->nCurrentPos = 1;
	return GP_OK;
}

/**
 * Задание текущей толщины линии
 * @param указатель на графический контекст
 * @param толщина в пикселях
 * @return код ошибки
 */
int SetThickness(TGraphicContext *ctx, char nThickness)
{
	if (nThickness <= 0) {
		ctx->nCurrentThickness = 1;
	} else if (nThickness >= 20) {
		ctx->nCurrentThickness = 20;
	} else {
		ctx->nCurrentThickness = nThickness;
	}

	ctx->nThOffsX = nThickness >> 1;
	ctx->nThOffsX100 = (nThickness * 100) / 2u;
	ctx->nThOffsY = ctx->nThOffsX * ctx->nBytesPerLine;

	if (ctx->nCurrentThickness > 5) {
		ctx->bRecalcThick = 1;
		ctx->bDrawAddonPixel = 1;
	} else {
		ctx->bRecalcThick = 0;

		if (ctx->nCurrentThickness == 4) {
			ctx->bSimpleRecalcThick = 1;
		} else {
			ctx->bSimpleRecalcThick = 0;
		}

		if (ctx->nCurrentThickness == 1)
			ctx->bDrawAddonPixel = 0;
		else
			ctx->bDrawAddonPixel = 1;
	}

   return GP_OK;
}

/**
 * Задание текущей маски линии
 * @param указатель на графический контекст
 * @param маска линии (единичные биты соответствуют разрешению отображения)
 * @param позиция окончания маски (по достижении этой позиции позиция проверки маски обнуляется)
 * @return код ошибки
 */
int SetLineMask(TGraphicContext *ctx, unsigned int nLineMask, unsigned int nLineMaskPos) {
	ctx->nLineMask = nLineMask;
	ctx->nCurrentPos = 1;

	if (nLineMaskPos <= 31)
	{
		ctx->nLineMaskPos = 1;

		while (nLineMaskPos) {
			ctx->nLineMaskPos <<= 1;
			--nLineMaskPos;
		}
	} else {
		ctx->nLineMaskPos = 0;
	}

	return GP_OK;
}
